
var m_monitor = {

	/**
	 * Object
	 * 直接存放当前要播放的 project 的原始数据.
	 * 这个数据是直接从服务器端发送过来的.
	 * 内部包含下面的 moduleEntries 数据.
	 */
	projectEntry: null,

	/**
	 * Array(Object)
	 * 直接存放当前要播放的 module 的原始数据.
	 * 这个数据是直接从服务器端发送过来的.
	 */
	moduleEntries: null,

	/**
	 * jquery-WebSocket 实例
	 * @version 0.2.0
	 *   现在直接从播放页进行播放的展示屏是没有 sid 的.
	 *   因此被禁止与后台建立 WebSocket, 也就没有这里的 ws 项了.
	 *   只有从等待页开始, 后来跳转到播放页的才存在 ws.
	 */
	ws: null,
	
	/**
	 * 存储 asset 的缓存. 每个元素包含 type, name, callbacks (Array).
	 */
	assetCache: [],
	
	/**
	 * Object
	 * WebSocket 处理的函数.
	 * key: string, 指 WebSocket 消息的 type
	 * value: function, 调用的函数. 调用时, 参数为 WebSocket 消息内容, this = m_monitor
	 */
	handlers: {},

	start: function () {
		if (params.displayType === "wait") { // 等待页
			this.handlers["/show/sidDispatcher"] = this.handleSid;
			this.handlers["/show/message"] = this.handleOrderInWait;
			
			this.buildSocket();
		} else {
			// 注册 handler
			this.handlers["/query-return/asset"] = this.handleAssets;
			this.handlers["/show/message"] = this.handleOrderInWait;
			
			m_data.init("../../js").then(function() {
				return m_monitor.queryModulesOfProject(params.pid);
			}).then(function(data) {
				var length = m_monitor.moduleEntries.length;
				if (length == 0) {
					return Promise.resolve(0);
				}

				var ps = [];
				for (var i = 0; i < length; i++) {
					ps.push(m_monitor.loadModuleScript(m_monitor.moduleEntries[i].path));
				}
				return Promise.all(ps);
			}).then(function() {
				m_monitor.finishInit();
			})
		}
	},

	/**
	 * 向后端发送展示屏播放状态.
	 * @param obj
	 *   相关的状态. 请参照 m_data 中的相关说明.
	 */
	sendStatus: function (obj) {
		try {
			m_monitor.ws.send('/status', obj);
		} catch (e) {
			console.error(e);
		}
	},

	sendQuery: function(obj) {
		try {
			m_monitor.ws.send('/query/asset', obj);
		} catch (e) {
			console.error(e);
		}
	},

	/**
	 * 函数, 用于获取资源参数,传递给m_data调用
	 * @param obj
	 * object
	 * {
	 *   type: &lt;type&gt;,
	 *   name: &lt;name&gt;
	 * }
	 * @param callback
	 * function(data)
	 *   完成查询之后的回调函数, data 是服务器返回的数据, object 格式
	 * @return 无
	 * @throw obj
	 *   如果 obj 不合法则就扔出来
	 */
	requireAssets: function(obj, callback) {
		var as = m_monitor.assetCache;
		var index = m_monitor.scanAssets(obj);
		if (index == -1) {
			index = as.length;
			as.push({
				type: obj.type,
				name: obj.name,
				callbacks: []
			});
		}
		
		as[index].callbacks.push(callback);
		
		m_monitor.sendQuery(obj);
		
//		while(m_monitor.assetCache === null); 简直变态的写法
//		callback(m_monitor.assetCache);
	},
	
	/**
	 * 函数, 用于获取资源参数, 传递给 m_data 调用.
	 * 用于兼容没有 sid 的播放. 这类播放情况不能使用 WebSocket 进行请求.
	 * 
	 * 参数、返回结果与 requireAssets 完全一样
	 */
	requireAssetsByAjax: function(obj, callback) {
		$.get("/aurora/show/do/queryAsset", {
			name: obj.name,
			type: obj.type
		}, function (data) {
			callback(data.data);
		})
	},
	
	/**
	 * 在请求列表中寻找对应的资源, 返回 m_monitor.assetCache 的索引
	 * 找不到, 则返回 -1
	 */
	scanAssets: function(obj) {
		var as = m_monitor.assetCache;
		var index = -1;
		for (var i = 0; i < as.length; i++) {
			if (as[i].type == obj.type && as[i].name == obj.name) {
				index = i;
			}
		}
		return index;
	},

	/**
	 * 查询已知 pid 对应的 project 的所有 modules 的信息.
	 * @param pid
	 *   Number, project id
	 * @return Promise 实例
	 */
	queryModulesOfProject: function (pid) {
		return new Promise(function (resolve, reject) {
			$.get("/aurora/show/do/queryProject", {pid: pid}, function (result) {
				m_monitor.projectEntry = result.datas;
				m_monitor.moduleEntries = result.datas.modules;
				resolve(result.datas);
			});
		});
	},

	/**
	 * 读取 module 文件
	 * @return
	 *   Promise 实例
	 */
	loadModuleScript: function (path) {
		return new Promise (function (resolve, reject) {
			$.getScript("/js/modules/" + path, function() {
				resolve(path);
			});
		});
	},

	/**
	 * 从远端加载的 module 的顺序很有可能是混乱的, 需要进行排序.
	 */
	sortModules: function () {
		var len = this.moduleEntries.length;
		for (var i = 0; i < len; i++) {
			var name = this.moduleEntries[i].name;
			m_data.m.addModuleOrder(name, name);
		}
	},

	/**
	 * 建立 WebSocket 连接
	 * @return
	 *   Promise 实例
	 */
	buildSocket: function () {
		var path;
		var once = false;
		if (params.displayType == "wait") {
			path = "ws://" + location.host + "/aurora/wait";
		} else {
			path = "ws://" + location.host + "/aurora/screen/" + params.sid;
		}
		return new Promise(function (resolve, reject) {
			m_monitor.ws = $.websocket(path, {
				open: function () {
					console.log("连接状态监视服务器成功");
					resolve();
					once = true;
				},
				close: function () {
					console.log("与远程状态监视服务器的连接关闭");
				},
				error: function () {
					if (!once) {
						reject();
					}
					console.error("与远程状态监视服务器的连接出现问题");
				},
				message: function (msg) {
					var msg = eval("(" + msg.originalEvent.data + ")");
					m_monitor.handleWebSocketMessage(msg);
				}
			});
		});
	},

	finishInit: function () {
		// 重置 m_data 中交互相关的函数
		if (params.sid != null) {
			m_data.fSendStatus = m_monitor.sendStatus;
			m_data.fRequireAssets = m_monitor.requireAssets;
		} else {
			m_data.fRequireAssets = m_monitor.requireAssetsByAjax;
		}
		
		// 配置 v0.2.0
		try {
			m_data.config.mapUrl = "/aurora/img/";
		} catch (e) {}

		// 设置自定义配置数据 v0.1.1
		this.writeSequenceConfig();

		m_monitor.sortModules();
		
		// 根据 sid 是否存在来选择是否建立 WebSocket | v0.2.0
		if (params.sid != null) {
			m_monitor.buildSocket().then(function () {
				m_monitor.sendStatus({
					status: "ready",
					displayType: params.displayType,
					pid: params.pid
				});
				// m_data: 启动模块的播放引擎
				startRender();
			});
		} else {
			startRender();
		}
	},

	/**
	 * 向 m_data 中注入自定义配置数据
	 * @since 0.1.1
	 */
	writeSequenceConfig: function () {
		// options 部分
		for (key in this.projectEntry.options) {
			var ikey = parseInt(key);
			if (isNaN(ikey)) {
				m_data.m.nameConfig[key] = this.projectEntry.options[key];
				continue;
			}
			if (ikey < 0) {
				continue;
			}

			m_data.m.sequenceConfig[ikey] = this.projectEntry.options[key];
		}

		// texts 部分 (可能为 null)
		// 补充文字配置部分
		if (this.projectEntry.texts != null) {
			var texts = this.projectEntry.texts;
			var len = texts.length;
			for (var i = 0; i < len; i++) {
				var text = texts[i];
				if (text == null) {
					continue;
				}

				var config = m_data.m.sequenceConfig[i];
				if (config == null) {
					m_data.m.sequenceConfig[i] = config = {};
				}
				config["_text"] = text;
			}
		}
	},
	
	/**
	 * 处理从 WebSocket 拿到的数据.
	 */
	handleWebSocketMessage : function(message) {
		var type = message.type;
		if (this.handlers[type] != null) {
			this.handlers[type].call(m_monitor, message.data);
		} else {
			console.log("从服务器那里收到消息: " + type);
			console.log(message);
		}
	},
	
	/**
	 * 获取到资源的解决方式
	 */
	handleAssets: function(asset) {
		var index = m_monitor.scanAssets(asset);
		if (index == -1) {
			return;
		}
		
		var callbacks = m_monitor.assetCache[index].callbacks;
		for (var i = 0; i < callbacks.length; i++) {
			try {
				callbacks[i](asset);
			} catch (e) {}
		}
		
		// remove
		m_monitor.assetCache.splice(index, 1);
	},
	
	/**
	 * 获取 sid. 等待页限定
	 */
	handleSid : function(data) {
		params.sid = data.sid;
		if (params.displayType == "wait") {
			showSid(); // 这个方法在 waiting.html 中
		}
	},
	
	/**
	 * 后端向前端发送指令
	 * 等待页处理方法 (现在播放页暂时也这样处理)
	 */
	handleOrderInWait : function(data) {
		var pid = data.value.pid;
		this.sendStatus({
			status: "ready",
			displayType: params.displayType,
			pid: pid
		});
		this.jumpToProject(pid);
	},
	
	/**
	 * 处理跳转
	 */
	jumpToProject : function(pid) {
		if (params.sid == null) {
			throw {title : "屏幕号 sid 没有指定", value : params};
		}
		location.href = "/aurora/project/" + pid + "?sid=" + params.sid;
	}
}

window.onload = function () {
	m_monitor.start.call(m_monitor);
}
